package com.bmslz.monitor.service.impl;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bmslz.monitor.base.util.StringUtils;
import com.bmslz.monitor.base.config.excep.MyException;
import com.bmslz.monitor.base.dto.AlarmModelSaveDto;
import com.bmslz.monitor.base.entity.AlarmModel;
import com.bmslz.monitor.base.entity.AlarmModelRule;
import com.bmslz.monitor.base.entity.Project;
import com.bmslz.monitor.mapper.AlarmModelMapper;
import com.bmslz.monitor.service.IAlarmModelRuleService;
import com.bmslz.monitor.service.IAlarmModelService;
import com.bmslz.monitor.service.IProjectService;
import com.bmslz.monitor.base.vo.AlarmModelVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @Description: 告警模板
 * @Author: bobo
 * @Date: 2025-08-10
 * @Version: V1.0
 */
@Slf4j
@Service
public class AlarmModelServiceImpl extends ServiceImpl<AlarmModelMapper, AlarmModel> implements IAlarmModelService {
    @Autowired
    private IAlarmModelRuleService alarmModelRuleService;
    @Lazy
    @Autowired
    private IProjectService projectService;

    @Override
    public AlarmModel getByNo(String alarmModelNo) {
        if (StringUtils.isEmpty(alarmModelNo)) {
            return null;
        }
        return this.getOne(new LambdaQueryWrapper<AlarmModel>().eq(AlarmModel::getModelNo, alarmModelNo), false);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void add(AlarmModelSaveDto dto) {
        dto.checkByAdd();
        AlarmModel alarmModel = new AlarmModel();
        alarmModel.setModelNo(dto.getModelNo());
        alarmModel.setName(dto.getName());
        alarmModel.setMonitorModelId(dto.getMonitorModelId());
        alarmModel.setNotifyData(dto.getNotifyData());
        alarmModel.setNotifyType(dto.getNotifyType());
        baseMapper.insert(alarmModel);

        List<AlarmModelRule> rules = dto.getRules().stream().map(item -> {
            AlarmModelRule rule = new AlarmModelRule();
            rule.setAlarmModelNo(dto.getModelNo());
            rule.setAlarmModelId(alarmModel.getId());
            rule.setRuleNo(StringUtils.randNo());
            rule.setAttKey(item.getAttKey());
            rule.setAttKeyType(item.getAttKeyType());
            rule.setConditionTag(item.getConditionTag());
            rule.setGrade(item.getGrade());
            rule.setDataValue(item.getDataValue());
            rule.setSilenceTime(item.getSilenceTime());
            return rule;
        }).collect(Collectors.toList());
        if (rules.size() < 1) {
            throw new MyException(500, "详细规则不能为空");
        }
        alarmModelRuleService.saveBatch(rules);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void myUpdateById(AlarmModelSaveDto dto) {
        dto.checkByEdit();
        AlarmModel dbModel = this.getById(dto.getId());
        if (dbModel == null) {
            throw new MyException(500, "不存在的模板");
        }
        List<AlarmModelRule> oldRules = alarmModelRuleService.getByModelId(dto.getId());
        List<AlarmModelRule> newRules = dto.getRules();
        AlarmModel alarmModel = new AlarmModel();
        alarmModel.setModelNo(dto.getModelNo());
        alarmModel.setName(dto.getName());
        alarmModel.setMonitorModelId(dto.getMonitorModelId());
        alarmModel.setNotifyData(dto.getNotifyData());
        alarmModel.setNotifyType(dto.getNotifyType());
        alarmModel.setId(dto.getId());
        baseMapper.updateById(alarmModel);

        //根据id list1<AlarmModelRule>筛选出没有id的对象，证明是要新增的规则
        //根据id 比较出list1<AlarmModelRule>有的list2<AlarmModelRule>没有的说明要删除规则
        //根据id 比较出list1<AlarmModelRule>有的list<AlarmModelRule>也有的说明要走新增
        // 将 oldRules 转换为以 id 为键的 Map，便于快速查找
        Map<String, AlarmModelRule> oldRulesMap = oldRules.stream()
                .filter(rule -> rule.getId() != null)
                .collect(Collectors.toMap(AlarmModelRule::getId, rule -> rule));

        // 1. 找出需要新增的规则：newRules 中 id 为 null 或不在 oldRulesMap 中的规则
        List<AlarmModelRule> toAdd = newRules.stream()
                .filter(rule -> rule.getId() == null || !oldRulesMap.containsKey(rule.getId()))
                .collect(Collectors.toList());

        // 2. 找出需要删除的规则：oldRules 中 id 不在 newRules 的 id 集合中的规则
        // 先获取 newRulesIds 的所有 id 集合（排除 null）
        List<String> newRulesIds = newRules.stream()
                .map(AlarmModelRule::getId)
                .filter(Objects::nonNull)
                .collect(Collectors.toList());

        List<AlarmModelRule> toDelete = oldRules.stream()
                .filter(rule -> !newRulesIds.contains(rule.getId()))
                .collect(Collectors.toList());

        // 3. 找出需要更新的规则：list1 和 list2 中 id 相同的规则
        List<AlarmModelRule> toUpdate = newRules.stream()
                .filter(rule -> rule.getId() != null && oldRulesMap.containsKey(rule.getId()))
                .collect(Collectors.toList());

        log.info("新增规则: " + toAdd);
        log.info("删除规则: " + toDelete);
        log.info("更新规则: " + toUpdate);
        List<AlarmModelRule> addRules = toAdd.stream().map(item -> {
            AlarmModelRule rule = new AlarmModelRule();
            rule.setGrade(item.getGrade());
            rule.setAlarmModelNo(dbModel.getModelNo());
            rule.setAlarmModelId(dbModel.getId());
            rule.setRuleNo(StringUtils.randNo());
            rule.setAttKey(item.getAttKey());
            rule.setAttKeyType(item.getAttKeyType());
            rule.setConditionTag(item.getConditionTag());
            rule.setSilenceTime(item.getSilenceTime());
            rule.setDataValue(item.getDataValue());
            return rule;
        }).collect(Collectors.toList());
        if (addRules.size() > 0) {
            alarmModelRuleService.saveBatch(addRules);

        }
        List<AlarmModelRule> updateRules = toUpdate.stream().map(item -> {
            AlarmModelRule rule = new AlarmModelRule();
            rule.setId(item.getId());
            rule.setAlarmModelId(dbModel.getId());
            rule.setAlarmModelNo(dbModel.getModelNo());
            rule.setAttKeyType(item.getAttKeyType());
            rule.setRuleNo(StringUtils.randNo());
            rule.setAttKey(item.getAttKey());
            rule.setConditionTag(item.getConditionTag());
            rule.setGrade(item.getGrade());
            rule.setSilenceTime(item.getSilenceTime());
            rule.setDataValue(item.getDataValue());
            return rule;
        }).collect(Collectors.toList());
        if (updateRules.size() > 0) {
            alarmModelRuleService.updateBatchById(updateRules);
        }
        if (toDelete.size() > 0) {
            alarmModelRuleService.removeBatchByIds(toDelete.stream().map(AlarmModelRule::getId).collect(Collectors.toList()));
        }
    }

    @Override
    public AlarmModelVo customGetById(String id) {
        if (StringUtils.isEmpty(id)) {
            return null;
        }
        AlarmModelVo res = new AlarmModelVo();
        AlarmModel alarmModel = baseMapper.selectById(id);
        if (alarmModel == null) {
            return null;
        }
        BeanUtils.copyProperties(alarmModel, res);
        res.setRules(alarmModelRuleService.getByModelId(alarmModel.getId()));
        return res;
    }

    @Override
    public List<AlarmModelRule> queryRuleByKey(String projectId, String attKey) {
        List<AlarmModelRule> res = new ArrayList<>();
        //查询项目绑定的告警模型，给出预警线
        Project project = projectService.getById(projectId);
        if (project == null) {
            return res;
        }
        String alarmModelId = project.getAlarmModelId();
        return alarmModelRuleService.queryRuleByKey(alarmModelId, attKey);
    }

    @Override
    public Long total(Date startTime, Date endTime) {
        LambdaQueryWrapper<AlarmModel> wrapper = new LambdaQueryWrapper<AlarmModel>();
        if (startTime != null) {
            wrapper.ge(AlarmModel::getCreateTime, startTime);
        }
        if (endTime != null) {
            wrapper.le(AlarmModel::getCreateTime, endTime);
        }
        return this.count(wrapper);
    }
}
